home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / unix / expnntp / expire.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-24  |  20.7 KB  |  770 lines

  1. /* Expire old messages.
  2.  * Inspired by 'expire.c' by Bernie Roehl.
  3.  * Substantially rewritten for integration into KA9Q NOS,
  4.  * WG7J v1.01 and later
  5.  * by Johan. K. Reinalda, WG7J/PA3DIS, March/April 92
  6.  *
  7.  * Old bid expiry by WG7J, March 92
  8.  * Index file support for jnos 1.10x3 and later added. WG7J Summer 1993
  9.  */
  10. /* 'expire #' sets the expire interval for n hours.
  11.  * Each time the timer goes off, a new process is started,
  12.  * that expires the old messages...
  13.  *
  14.  * The control file '~/spool/expire.dat' contains lists of
  15.  * filename age
  16.  * where 'filename' is the name of the .txt file under '~/spool/mail'
  17.  * containing the messages (without the .txt extension) and where
  18.  * age = maximum age of message in days.
  19.  *
  20.  * If no age is given, the default age is 21 days.
  21.  *
  22.  * filename can be extended into subdirectories, and can have either
  23.  * '/', '\' or '.' to indicate subdirectories.
  24.  * filename should NOT have the ending '.txt'
  25.  */
  26.  
  27. /* 25 March 1994
  28.  * Expire now expires messages stored in the NNTP server format.
  29.  * the additions to expire.dat 'sort of' follow the conventions for nntp
  30.  * in other areas of nos, namely the delimiter for nntp groups is bang (!)
  31.  * thus the format for expiring news items is
  32.  * !<newsgroup-name> age     for example  !ampr.vk5 21
  33.  * age still defaults to 21 days if not specified.
  34.  * automatic active and history file updates are done on each firing of
  35.  * the expiry process, only if at least one newsgroup was processed.
  36.  * all records are treated the same in the history file, and are expired
  37.  * when older than the largest number of days specified for any newsgroup.
  38.  * numbers of expiries in groups and history are both logged if > 0
  39.  * if you have any ideas on the above, dont keep them to yourself !! ;-)
  40.  * this code is not rigorously tested :-) vk5xxx march 1994
  41.  *
  42.  * April 7 1994
  43.  * added test for articles to ensure they are numeric. nos uses other control
  44.  * files in the newsgroup area (namely news.rc), nntp_expire would expire
  45.  * these happily, and update_nntp_active wqas confused by them (start = 0)
  46.  *
  47.  * June 25 1994
  48.  * Update the news.rc file with the lowest article number when the news.rc
  49.  * number is invalid.
  50.  */
  51.  
  52. #ifdef MSDOS
  53. #include <dir.h>
  54. #include <dos.h>
  55. #endif
  56. #include <alloc.h>
  57. #include <fcntl.h>
  58. #include <sys/stat.h>
  59. #include <sys/timeb.h>
  60. #include <ctype.h>
  61. #include <time.h>
  62. #include "global.h"
  63. #include "timer.h"
  64. #include "proc.h"
  65. #include "bm.h"
  66. #include "files.h"
  67. #include "smtp.h"
  68. #include "socket.h"
  69. #include "index.h"
  70. #ifdef NNTPS
  71. #include "dirutil.h"   /* NNTP expire */
  72. #include <limits.h>    /* NNTP expire */
  73. #endif
  74.  
  75. #ifdef LINUX
  76. extern int unlink __ARGS((char *));
  77. #endif
  78.  
  79. #ifndef __BORLANDC__
  80. #ifndef UNIX
  81. time_t mktime(struct tm *);
  82.  
  83. /* If you're using BC++ 2.0 or higher, you don't need this,
  84.  * but TCC 2.0 doesn't have it... (But, TC++ v1.01 does)
  85.  */
  86. /* Simple emulation of the mktime() call (sort-of works :-) )
  87.  * doesn't do any error checking,
  88.  * no timezone adjustments or value adjustments
  89.  * Simply 'sort-a' calculates the number of seconds since 1970 - WG7J
  90.  */
  91. time_t
  92. mktime(t)
  93. struct tm *t;
  94. {
  95.     static int total[12]={0,31,59,90,120,151,181,212,243,273,304,334};
  96.     int years;
  97.     int leapyears;
  98.     int days;
  99.  
  100.     /* time count start at jan 1, 1970, 00:00 hrs */
  101.     years = t->tm_year - 70;
  102.     /* adjust for leap-years */
  103.     leapyears = (years + 2) / 4;
  104.     if (!((years+70) & 3) && (t->tm_mon < 2))
  105.     --leapyears;    /* no extra day until 3/1 or after */
  106.  
  107.     days = years*365L + leapyears + total[t->tm_mon] + (t->tm_mday-1);
  108.  
  109.     return( days*86400L + t->tm_hour*3600L + t->tm_min*60L + t->tm_sec);
  110. }
  111. #endif  /* UNIX */
  112. #endif  /* __BORLANDC__ */
  113.  
  114. /* Include the rest in #ifdef's, so we don't pull in the whole module
  115.  * when we only have AT defined and NOT EXPIRE !
  116.  * (since these are the two modules that needs the surrogate mktime()
  117.  *  if compiling with Turbo C 2.0 )
  118.  */
  119. #ifdef EXPIRY
  120.  
  121. /* Default expiry values: */
  122. #define DEFAULT_AGE 21       /* 21 days from origination date */
  123. #define MSPHOUR (1000L*60L*60L)
  124. static struct timer Expiretimer;
  125. static int Eproc;
  126.  
  127. static void Expireprocess __ARGS((int a,void *v1,void *v2));
  128. static void Expiretick __ARGS((void *));
  129. static void expire __ARGS((char *,int));
  130. static void Oldbidtick __ARGS((void *p));
  131.  
  132. #ifdef NNTPS /* for NNTP expiry */
  133. /* NNTP Expire stuff */
  134. static void expire_nntp __ARGS((char *, int));
  135. static void update_nntp_history __ARGS((int));
  136. static void update_nntp_active __ARGS((void));
  137. static void update_newsrc __ARGS((int, int, char *));
  138. static int convert_num __ARGS((char **));
  139. static char *newsgroup_to_path __ARGS((char *));
  140. #endif
  141.  
  142. int
  143. doexpire(argc,argv,p)
  144. int argc;
  145. char *argv[];
  146. void *p;
  147. {
  148.     if(argc < 2) {
  149.         tprintf("timer: %lu/%lu hrs\n",
  150.             read_timer(&Expiretimer)/MSPHOUR,
  151.             dur_timer(&Expiretimer)/MSPHOUR);
  152.         return 0;
  153.     }
  154.     if(*argv[1] == 'n') {
  155.         Expiretick(NULL);
  156.         return 0;
  157.     }
  158.     /* set the timer */
  159.     stop_timer(&Expiretimer); /* Just in case */
  160.     Expiretimer.func = (void (*)())Expiretick;/* what to call on timeout */
  161.     Expiretimer.arg = NULL;        /* dummy value */
  162.     set_timer(&Expiretimer,atol(argv[1])*MSPHOUR); /* set timer duration */
  163.     start_timer(&Expiretimer);
  164.     return 0;
  165. }
  166.  
  167. void
  168. Expiretick(p)
  169. void *p;
  170. {
  171.     start_timer(&Expiretimer);
  172.     /* Spawn off the process */
  173.     if(!Eproc)
  174.         newproc("Expiry", 1536, Expireprocess, 0, NULL, NULL, 0);
  175. }
  176.  
  177. static void
  178. Expireprocess(a,v1,v2)
  179. int a;
  180. void *v1, *v2;
  181. {
  182.     char line[80];
  183.  
  184. #ifdef NNTPS /* for NNTP expiry */
  185.     int expire_nntp_history = 0;
  186. #endif
  187.     int age;
  188.     char *cp;
  189.     FILE *ctl;
  190.  
  191.     Eproc = 1;
  192.     if ((ctl = fopen(Expirefile, "r")) == NULLFILE) {
  193.         Eproc = 0;
  194.         return;
  195.     }
  196.     /* read lines from the control file */
  197.     while(fgets(line, sizeof(line), ctl) != NULLCHAR) {
  198.         pwait(NULL); /* be nice */
  199. #ifdef NNTPS /* for NNTP expiry */
  200.         if((*line == '#') || (*line == '\n')) /* comment or blank line */
  201. #endif
  202. #ifndef NNTPS /* for NNTP expiry */
  203.         if((*line == '#') || (*line == '!') || (*line == '\n')) /* comment or blank line */
  204. #endif
  205.             continue;
  206.         rip(line);
  207.         /* terminate area name */
  208.         age = DEFAULT_AGE;
  209.         if((cp=strchr(line, ' ')) != NULLCHAR) {
  210.             /* there is age info */
  211.             *cp++ = '\0';
  212.             age = atoi(cp);
  213.         }
  214.         pwait(NULL); /* be nice */
  215. #ifdef NNTPS /* for NNTP expiry */
  216.         if (*line == '!')   /* Expire NNTP entry if line begins with ! */ {
  217.             expire_nntp(&line[1], age);
  218.             if (expire_nntp_history < age)
  219.                 expire_nntp_history = age;
  220.         }
  221.         else
  222. #endif
  223.             expire(line, age);
  224.     }
  225.     fclose(ctl);
  226. #ifdef NNTPS /* for NNTP expiry */
  227.     if (expire_nntp_history) {
  228.         update_nntp_history(expire_nntp_history);
  229.         update_nntp_active();  /* and news.rc */
  230.     }
  231. #endif
  232.     Eproc = 0;
  233. }
  234.  
  235. #ifdef NNTPS /* for NNTP expiry */
  236. /*
  237.         expire nntp articles - the kludgomatic version.
  238.         these routines probably arent memory friendly, but they work.
  239.         more work needs to be done to make it meld more nicely into nos.
  240.         coded by brett england (future amateur) and rob vk5xxx.
  241. */
  242. static
  243. int
  244. convert_num( p )
  245. char **p;
  246. {
  247.     int i;
  248.     char *pp = *p;
  249.  
  250.     i = (*pp - '0') * 10 + *(pp+1) - '0';
  251.     (*p)+=2;
  252.     return (i);
  253. }
  254.  
  255. static
  256. void
  257. update_nntp_history(age)
  258. int age;
  259. {
  260.   FILE *old, *new;
  261.   char bckfile[FILE_PATH_SIZE];
  262.   char line[LINELEN];
  263.   char *p;
  264.   struct tm tm_time;
  265.   time_t expire_time, file_time, now;
  266.   int history_records_expired = 0;
  267.  
  268.   while(mlock(Newsdir,"history"))
  269.     pause(10000L);
  270.  
  271.  
  272.   expire_time = (long)age * 24L * 60L * 60L;  /* days to seconds */
  273.   time(&now);
  274.  
  275.   /* Rename the history file, and use it to expire messages
  276.   */
  277.   sprintf(bckfile,"%s.bak",History);
  278.   unlink(bckfile);
  279.   if(rename(History,bckfile) == -1) {
  280.       log(-1,"NNTP Expire History: Can't rename %s to %s",History, bckfile);
  281.       return;
  282.   }
  283.  
  284.   if((old = fopen(bckfile,"rt")) == NULL) {
  285.       log(-1,"NNTP Expire History: Can't open file %s for read", bckfile);
  286.       return;
  287.   }
  288.  
  289.   if((new = fopen(History,"wt")) == NULL) {
  290.       log(-1,"NNTP Expire History: Can't open file %s for write",History);
  291.       return;
  292.   }
  293.  
  294.   for(;;) {
  295.     if(fgets(line, LINELEN, old) == NULL)
  296.         break;
  297.  
  298.     p = line;
  299.     while (*p != '\0' && *p != '>')
  300.        p++;
  301.     if (*p != '>')  /* Some sort of corrupt line */
  302.         continue;
  303.     p+=2;
  304.     tm_time.tm_year = convert_num(&p);
  305.     tm_time.tm_mon = convert_num(&p)-1;
  306.     tm_time.tm_mday = convert_num(&p);
  307.     p++;   /* Point to time component */
  308.     tm_time.tm_hour = convert_num(&p);
  309.     tm_time.tm_min = convert_num(&p);
  310.     tm_time.tm_sec = 0;
  311.  
  312.     file_time = mktime(&tm_time);
  313.  
  314.     if (file_time > ( now - expire_time ))
  315.       fputs(line, new);
  316.     else
  317.       history_records_expired++;
  318.   }
  319.  
  320.   fclose(new);
  321.   fclose(old);
  322.   rmlock(Newsdir,"history");
  323.  
  324.   if(history_records_expired > 0)
  325.     log(-1,"NNTP History: Expired %d records ...", history_records_expired);
  326. }
  327.  
  328. /* Check the news.rc file if the number contained is less than min
  329.    replace the news.rc file contents with min.
  330. */
  331. static
  332. void
  333. update_newsrc( min, max, path )
  334.   int min, max;
  335.   char *path;
  336. {
  337.   FILE *newsrcfd;
  338.   char news_file[FILE_PATH_SIZE], line[LINELEN];
  339.   int no;
  340.  
  341.   sprintf(news_file,"%s/news.rc",path);
  342.   if((newsrcfd = fopen(news_file,"rt")) == NULL) {
  343.       log(-1,"NNTP News: Can't open file %s for read", news_file);
  344.       return;
  345.   }
  346.  
  347.   fgets(line, LINELEN, newsrcfd);
  348.   fclose(newsrcfd);
  349.  
  350.   no = atoi(line);
  351.   if((no < min) || (no > max)) {
  352.     if((newsrcfd = fopen(news_file,"wt")) == NULL) {
  353.         log(-1,"NNTP News: Can't open file %s for write", news_file);
  354.         return;
  355.     }
  356.     fprintf(newsrcfd,"%d",min);
  357.     fclose(newsrcfd);
  358.   }
  359. }
  360.  
  361. /* Update the active file and return the lowest news article
  362. */
  363. static
  364. void
  365. update_nntp_active() {
  366.   FILE *old, *new;
  367.   char bckfile[FILE_PATH_SIZE];
  368.   char line[LINELEN];
  369.   char *p, *path, *rpath;
  370.   int min, max;
  371.   int command, file_num;
  372.   struct ffblk *file;
  373.  
  374.   /* Rename the active file
  375.   */
  376.   sprintf(bckfile,"%s.bak",Active);
  377.   unlink(bckfile);
  378.   if(rename(Active,bckfile) == -1) {
  379.       log(-1,"NNTP Active: Can't rename %s to %s",Active, bckfile);
  380.       return;
  381.   }
  382.  
  383.   if((old = fopen(bckfile,"rt")) == NULL) {
  384.       log(-1,"NNTP Active: Can't open file %s for read", bckfile);
  385.       return;
  386.   }
  387.  
  388.   if((new = fopen(Active,"wt")) == NULL) {
  389.       log(-1,"NNTP Active: Can't open file %s for write",Active);
  390.       return;
  391.   }
  392.  
  393.   file = (struct ffblk *)mallocw(sizeof(struct ffblk));
  394.   for(;;) {
  395.     if(fgets(line, LINELEN, old) == NULL)
  396.        break;
  397.  
  398.     p = line;
  399.     while (*p != '\0' && ! isspace(*p))
  400.       p++;
  401.     if (*p == '\0')
  402.       continue;
  403.     *p = '\0';
  404.  
  405.     p++;
  406.     while(*p != '\0' && (isspace(*p) || isdigit(*p)))
  407.         p++;
  408.  
  409.     rpath = newsgroup_to_path( line );
  410.     path = wildcardize(rpath);
  411.  
  412.     command = 0;
  413.     min = INT_MAX;
  414.     max = INT_MIN;
  415.     for(;;) {
  416.         if (!nextname(command, path, file))
  417.             break;
  418.         command = 1;   /* Find next */
  419.  
  420.         if (file->ff_name[0] == '.')
  421.             continue;  /* ignore . and .. */
  422.  
  423.         file_num = atoi(file->ff_name);
  424.         if (file_num > 0) {     /* should always be greater than 0 */
  425.             if (file_num < min)
  426.                 min = file_num;
  427.             if (file_num > max)
  428.                 max = file_num;
  429.         } 
  430.     }
  431.     if (min == INT_MAX && max == INT_MIN) { /* No files */
  432.         min = 1;
  433.         max = 0;
  434.     }
  435.     update_newsrc(min, max, rpath);
  436.  
  437.     fprintf(new,"%s %05d %05d %s", line, max, min, p);
  438.   }
  439.   fclose(new);
  440.   fclose(old);
  441.   free(file);
  442. }
  443.  
  444. static
  445. char
  446. *newsgroup_to_path( group )
  447. char *group;
  448. {
  449.     FILE *f;
  450.     static char line[LINELEN];
  451.     char *cp;
  452.  
  453.     if((f = fopen(Pointer,"r")) == NULL)
  454.         return((char *)NULL);
  455.  
  456.     for (;;) {
  457.         if (fgets(line,LINELEN,f) == NULL)
  458.             break;
  459.  
  460.         if (strcspn(line," ") != strlen(group))
  461.             continue;
  462.  
  463.         if (strnicmp(group,line,strlen(group)) == 0) {
  464.             cp = (strchr(line,' ')) + 1;
  465.  
  466.             rip(cp);
  467.             fclose(f);
  468.             return (cp);
  469.         }
  470.     }
  471.     fclose(f);
  472.     return (NULL);
  473. }
  474.  
  475. void
  476. expire_nntp(nntp_name, age)
  477. char *nntp_name;
  478. int age;
  479. {
  480.     char *path;
  481.     int command = 0;
  482.     struct ffblk file;
  483.     struct tm tm_time;
  484.     time_t file_time, expire_time, now;
  485.     char nntp_file[FILE_PATH_SIZE];
  486.     char save_path[FILE_PATH_SIZE];
  487.     int file_num, expired = 0;
  488.  
  489.     /* Resolve nntp name into directory path */
  490.     if ((path = newsgroup_to_path( nntp_name )) == NULL)
  491.         return;
  492.  
  493.     expire_time = (long)age * 24L * 60L * 60L;
  494.     time(&now);
  495.     strcpy(save_path, path);
  496.     path = wildcardize(path);
  497.  
  498.     for(;;) {
  499.         if (!nextname(command, path, &file))
  500.             break;
  501.         command = 1;   /* Find next */
  502.  
  503.         if (file.ff_name[0] == '.')
  504.             continue;  /* ignore . and .. */
  505.  
  506.         file_num = atoi(file.ff_name);
  507.         /* should always be greater than 0 for a valid nntp article */
  508.         if (file_num > 0) {
  509.             /* only expire a file that is numeric :-) */
  510.             tm_time.tm_sec = 0;  /* DOS doesn't store this */
  511.             tm_time.tm_min = (file.ff_ftime >> 5) & 0x3f;
  512.             tm_time.tm_hour = (file.ff_ftime >> 11) & 0x1f;
  513.             tm_time.tm_mday = file.ff_fdate & 0x1f;
  514.             tm_time.tm_mon = ((file.ff_fdate >> 5) & 0xf)-1;
  515.             tm_time.tm_year = (file.ff_fdate >> 9) + 80;
  516.  
  517.             file_time = mktime(&tm_time);
  518.  
  519.             if (( now - expire_time ) > file_time) {
  520.                  sprintf(nntp_file, "%s/%s", save_path, file.ff_name);
  521.                  unlink(nntp_file);
  522.                  expired++;
  523.             }
  524.         }
  525.     }
  526.     if(expired)
  527.         log(-1,"NNTP Expired: %d in %s",expired, nntp_name);
  528. }
  529. #endif /*nntps*/
  530.  
  531. void
  532. expire(filename,age)
  533. char *filename;
  534. int age;
  535. {
  536.     int oldidx,idx,expired,i;
  537.     FILE *old,*new;
  538.     long start,pos,msgsize;
  539.     time_t now;
  540.     struct indexhdr hdr;
  541.     struct mailindex index;
  542.     char file[FILE_PATH_SIZE];
  543.     char bckfile[FILE_PATH_SIZE];
  544.     char buf[LINELEN];
  545.  
  546.     dirformat(filename);
  547.  
  548.     if(mlock(Mailspool,filename))
  549.         /* can't get a lock */
  550.         return;
  551.  
  552.     /* Rename the index file, and use it to expire messages */
  553.     sprintf(file,"%s/%s.ind",Mailspool,filename);
  554.     sprintf(bckfile,"%s/%s.idn",Mailspool,filename);
  555.     unlink(bckfile);
  556.     if(rename(file,bckfile) == -1) {
  557.         rmlock(Mailspool,filename);
  558.         return;
  559.     }
  560.     /* Open the old index file */
  561.     if((oldidx=open(bckfile,READBINARY)) == -1) {
  562.         rmlock(Mailspool,filename);
  563.         return;
  564.     }
  565.     /* Create the new index file */
  566.     if((idx=open(file,CREATETRUNCATEBINARY,CREATEMODE)) == -1) {
  567.         close(oldidx);
  568.         rmlock(Mailspool,filename);
  569.         return;
  570.     }
  571.  
  572.     /* Rename the text file */
  573.     sprintf(file,"%s/%s.txt",Mailspool,filename);
  574.     /* get the name for the backup file */
  575.     sprintf(bckfile,"%s/%s.bak",Mailspool,filename);
  576.  
  577.     /* rename the file */
  578.     unlink(bckfile);
  579.     if(rename(file,bckfile) == -1) {
  580.         close(oldidx);
  581.         close(idx);
  582.         rmlock(Mailspool,filename);
  583.         return;
  584.     }
  585.     /* open the backup file and the new txt file */
  586.     if((old=fopen(bckfile,"rb")) == NULL) {
  587.         close(oldidx);
  588.         close(idx);
  589.         rmlock(Mailspool, filename);
  590.         return;
  591.     }
  592.     if((new=fopen(file,"wt")) == NULL) {
  593.         close(oldidx);
  594.         close(idx);
  595.         fclose(old);
  596.         rmlock(Mailspool, filename);
  597.         return;
  598.     }
  599.  
  600.     /* Write a default header to the new index file */
  601.     default_header(&hdr);
  602.     write_header(idx,&hdr);
  603.  
  604.     memset(&index,0,sizeof(index));
  605.     time(&now);
  606.     start = pos = 0;
  607.     expired = 0;
  608.  
  609.     /* Read the header from the old index file */
  610.     read_header(oldidx,&hdr);
  611.  
  612.     /* Now read all messages, and expire old ones */
  613.     for(i = 0; i < hdr.msgs; i++) {
  614.         default_index(filename,&index);
  615.         read_index(oldidx,&index);
  616.         msgsize = index.size;
  617.         if(now - index.date < (age*86400L)) {
  618.             /* This one we should keep ! Copy it from old to new */
  619.             fseek(old,start,SEEK_SET);
  620.             pos = ftell(new);
  621.             /* Read the first line, should be 'From ' line */
  622.             bgets(buf,sizeof(buf),old);
  623.             fprintf(new,"%s\n",buf);
  624.             /* Now do the rest until we find another 'From ' or
  625.              * reach end of file.
  626.              */
  627.             while(bgets(buf,sizeof(buf),old) != NULL) {
  628.                 pwait(NULL);
  629.                 if(!strncmp(buf,"From ",5))
  630.                     break;
  631.                 fprintf(new,"%s\n",buf);
  632.             }
  633.             /* rewrite the index file for the message */
  634.             index.size = ftell(new) - pos;
  635.             WriteIndex(idx,&index);
  636.         } else
  637.             expired++;
  638.         start += msgsize;    /* starting offset of the next message */
  639.     }
  640.     default_index("",&index);
  641.     close(oldidx);
  642.     close(idx);
  643.     fclose(new);
  644.     fclose(old);
  645.     rmlock(Mailspool,filename);
  646.     if(expired)
  647.         log(-1,"Expired: %d in %s",expired,filename);
  648. }
  649.  
  650.  
  651. /******************************************************************/
  652. /* This program will deleted old BID's from the history file,
  653.  * after making a backup copy.
  654.  *
  655.  *  Eg. 'oldbids 24 30' will try to delete all bids older then 30 days
  656.  *      every 24 hours.
  657.  *      'oldbids now' will do it now, with set value of age.
  658.  *
  659.  * Copyright 1992, Johan. K. Reinalda, WG7J/PA3DIS
  660.  *      email : johan@ece.orst.edu
  661.  *      packet: wg7j@wg7j.or.usa.na
  662.  *
  663.  * Any part of this source may be freely distributed for none-commercial,
  664.  * amateur radio use only, as long as credit is given to the author.
  665.  *
  666.  * v1.0 920325
  667.  */
  668. static struct timer Oldbidtimer;
  669. static int Oldbid_age = 30;
  670.  
  671. static
  672. void
  673. Oldbidtick(p)
  674. void *p;
  675. {
  676.     int expired = 0;
  677.     char *cp;
  678.     FILE *old, *new;
  679.     time_t now;
  680.     time_t age;
  681.     time_t bidtime;
  682.     #define LEN 80
  683.     char oldfile[LEN];
  684.     char buf[LEN];
  685.  
  686.     stop_timer(&Oldbidtimer);
  687.  
  688.     /* delete a previous backup and rename current history file */
  689.     sprintf(oldfile,"%s.bak",Historyfile);
  690.     unlink(oldfile);
  691.     if(rename(Historyfile,oldfile) == -1) {
  692.         tputs("oldbid: can't rename history file\n");
  693.         return;
  694.     }
  695.  
  696.     /* open backup for reading, create new history file */
  697.     if((old = fopen(oldfile,"rt")) == NULL) {
  698.         tputs("oldbid: error opening history.bak\n");
  699.         return;
  700.     }
  701.     if((new = fopen(Historyfile,"wt")) == NULL) {
  702.         tputs("oldbid: error creating history file\n");
  703.         return;
  704.     }
  705.  
  706.     now = time(&now);
  707.     age = (time_t)(Oldbid_age*86400L);
  708.  
  709.     while(fgets(buf,LEN,old) != NULL) {
  710.         if((cp=strchr(buf,'\n')) != NULLCHAR)
  711.             *cp = '\0';
  712.         if((cp=strchr(buf,' ')) != NULLCHAR) {
  713.             /*found one with timestamp*/
  714.             *cp = '\0';
  715.             cp++;   /* now points to timestamp */
  716.             if((bidtime = atol(cp)) == 0L)
  717.                 /*something wrong, re-stamp */
  718.                 fprintf(new,"%s %ld\n",buf,now);
  719.             else {
  720.                 /* Has this one expired yet ? */
  721.                 if(now - bidtime < age)
  722.                     fprintf(new,"%s %ld\n",buf,bidtime);
  723.                 else
  724.                     expired++;
  725.             }
  726.         } else {
  727.             /* This is an old one without time stamp,
  728.              * add to the new file with current time as timestamp
  729.              */
  730.             fprintf(new,"%s %ld\n",buf,now);
  731.         }
  732.     }
  733.     fclose(old);
  734.     fclose(new);
  735.     if(expired)
  736.         log(-1,"Oldbids: %d expired",expired);
  737.     start_timer(&Oldbidtimer);
  738.     return;
  739. }
  740.  
  741. int
  742. dooldbids(argc,argv,p)
  743. int argc;
  744. char *argv[];
  745. void *p;
  746. {
  747.     if(argc < 2){
  748.     tprintf("timer: %lu/%lu hrs; age: %d days\n",
  749.         read_timer(&Oldbidtimer)/MSPHOUR,
  750.         dur_timer(&Oldbidtimer)/MSPHOUR,
  751.         Oldbid_age);
  752.     return 0;
  753.     }
  754.     if(*argv[1] == 'n') {
  755.     Oldbidtick(NULL);
  756.     return 0;
  757.     }
  758.     /* set the timer */
  759.     stop_timer(&Oldbidtimer); /* Just in case */
  760.     Oldbidtimer.func = (void (*)())Oldbidtick;/* what to call on timeout */
  761.     Oldbidtimer.arg = NULL;        /* dummy value */
  762.     set_timer(&Oldbidtimer,atol(argv[1])*MSPHOUR); /* set timer duration */
  763.     if(argc > 2)
  764.     Oldbid_age = atoi(argv[2]);
  765.     Oldbidtick(NULL); /* Do one now and start it all!*/
  766.     return 0;
  767. }
  768.  
  769. #endif /* EXPIRE */
  770.